package com.bub.pay.core.manager.user.impl;

import com.bub.pay.core.dao.*;
import com.bub.pay.core.entity.domain.SysPerm;
import com.bub.pay.core.entity.domain.SysRole;
import com.bub.pay.core.entity.domain.SysUser;
import com.bub.pay.core.entity.domain.UserLoginWhite;
import com.bub.pay.core.entity.vo.user.GoogleVo;
import com.bub.pay.core.entity.vo.user.LoginVo;
import com.bub.pay.core.entity.vo.user.PasswordVo;
import com.bub.pay.core.enums.SysLoginTypeEnum;
import com.bub.pay.core.manager.system.CurrentSystemManager;
import com.bub.pay.core.manager.system.SysLogManager;
import com.bub.pay.core.manager.user.UserCommonManager;
import com.bub.pay.core.manager.user.async.AsyncLogoutLogRunnable;
import com.bub.pay.framework.common.async.ExceptionHandlingAsyncTaskExecutor;
import com.bub.pay.framework.common.base.Cause;
import com.bub.pay.framework.common.config.UserConfig;
import com.bub.pay.framework.common.constant.SystemConstants;
import com.bub.pay.framework.common.entity.model.SessionModel;
import com.bub.pay.framework.common.exception.GlobalException;
import com.bub.pay.framework.common.util.*;
import com.bub.pay.framework.redis.RedisClient;
import com.bub.pay.framework.redis.RedisKeyConstant;
import com.bub.pay.framework.redis.RedisKeyPrefix;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Slf4j
@Service
public class UserCommonManagerImpl implements UserCommonManager {

    @Resource
    private SysUserDao sysUserDao;

    @Resource
    private SysLoginLogDao sysLoginLogDao;

    @Resource
    private RedisClient redisClient;

    @Resource
    private ExceptionHandlingAsyncTaskExecutor taskExecutor;

    @Resource
    private CurrentSystemManager currentSystemManager;

//    @Resource
//    private MerchantDao merchantDao;
//
//    @Resource
//    private AgentDao agentDao;

    @Resource
    private SysRoleDao sysRoleDao;

    @Resource
    private SysPermDao sysPermDao;
    @Autowired
    private SysLogManager sysLogManager;

    @Resource
    private UserConfig userConfig;


    @Resource
    private UserLoginWhiteDao userLoginWhiteDao;

    @Override
    @Transactional
    public SessionModel userLogin(HttpServletRequest httpRequest, LoginVo reqVo) {
        // 获取基本参数
        String reqIp = IpRemote.getIpAddr(httpRequest);

        //获取白名单列表
        SysUser sysUser = new SysUser();
        sysUser.setUserName(reqVo.getLoginName());
        sysUser.setUserType(reqVo.getUserType());
        sysUser = sysUserDao.selectOne(sysUser);

        int userType = reqVo.getUserType();
        Example whiteExample = new Example(UserLoginWhite.class);
        Example.Criteria whiteCriteria = whiteExample.createCriteria();
        whiteCriteria.andEqualTo("ip", reqIp).andEqualTo("userType", userType);
        if(userType != 3){
            whiteCriteria.andEqualTo("extendId", sysUser.getExtendId());
        }
        UserLoginWhite whiteObj = userLoginWhiteDao.selectOneByExample(whiteExample);

        try {
            if (whiteObj == null && (sysUser.getIsSys() == 1 || reqVo.getUserType() == 3)) {
                //sysUser.setUserBindIp(reqIp);
                //首次登录绑定白名单
                UserLoginWhite white = new UserLoginWhite();
                white.setExtendId(sysUser.getExtendId());
                white.setIp(reqIp);
                white.setUserType(sysUser.getUserType());
                white.setCreateDate(new Date());
                white.setCreateName(sysUser.getUserName());
                white.setCreateId(sysUser.getId());
                white.setLastLoginDate(new Date());
                userLoginWhiteDao.insert(white);

            }
            checkGoogle(sysUser.getId(), sysUser.getGoogleSecret(), reqVo.getGoogleCode());
            redisClient.delete(RedisKeyPrefix.USER, RedisKeyConstant.LOGIN_FAILD + sysUser.getId());
            SessionModel session = new SessionModel();
            if (sysUser.getUserType() != 2) {
                // 查询当前用户角色
                List<SysRole> listRoleEntity = sysRoleDao.queryRoleByUserId(sysUser.getId(), sysUser.getUserType());
                List<String> roleList = new ArrayList<>();
                StringBuilder sbRole = new StringBuilder();
                if (!CollectionUtil.isNullOrEmpty(listRoleEntity)) {
                    listRoleEntity.forEach(e -> {
                        roleList.add(e.getCode());
                        sbRole.append(e.getName()+",");
                    });
                }
                // 查询当前用户权限
                List<SysPerm> listPermEntity = sysPermDao.queryPermIdsByUserId(sysUser.getId(), sysUser.getUserType());
                List<String> permList = new ArrayList<>();
                if (!CollectionUtil.isNullOrEmpty(listPermEntity)) {
                    listPermEntity.forEach(e -> {
                        permList.add(e.getCode());
                    });
                }
                session.setRoleName(sbRole.substring(0,sbRole.length()-1));
                session.setRoleList(roleList);// 角色
                session.setCodeList(permList);// 权限
            }

            Subject subject = SecurityUtils.getSubject();
            Session s = subject.getSession(true);
            session.setAuthToken((String) s.getId());
            session.setLoginName(reqVo.getLoginName());
            session.setUserId(sysUser.getId());
            session.setExtendId(sysUser.getExtendId());
            session.setUserType(sysUser.getUserType());
            session.setBindEmail(sysUser.getBindEmail());
            session.setBindGoogle(sysUser.getBindGoogle());
            session.setBindPhone(sysUser.getBindPhone());
            session.setEmail(sysUser.getEmail());
            session.setGoogleSecret(sysUser.getGoogleSecret());
            session.setPhone(sysUser.getPhone());
            session.setLoginIp(reqIp);
            session.setIsSys(sysUser.getIsSys());
            session.setNickName(sysUser.getNickName());
            session.setResetFlag(sysUser.getResetFlag());
            session.setUserPassword(sysUser.getPassword());
            // 代理不需要设置交易密码
            if (!StringUtils.isBlank(sysUser.getUserPayPass()) || reqVo.getUserType() == 2) {
                session.setBindPayPass(1);
                session.setPayPass(sysUser.getUserPayPass());
            }
            String userId = session.getUserId().toString();
            String authToken = session.getAuthToken();

            ContextHolder.setAuthToken(authToken);

            String tokenIdentification = userConfig.getTokenIdentification();
            redisClient.set(RedisKeyPrefix.USER, buildUserAuthTokenKey(userId, authToken, tokenIdentification),
                    session, userConfig.getTokenExpire());

            String userKey = buildUserKey(userId, tokenIdentification);
            if (redisClient.exists(RedisKeyPrefix.USER, userKey)) {
                final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
                String hisTokenKey = buildHisTokenKey(hisToken, tokenIdentification);
                redisClient.set(RedisKeyPrefix.USER, hisTokenKey, hisToken, userConfig.getHisTokenExpire());
                String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);
                redisClient.delete(RedisKeyPrefix.USER, userHisTokenKey);
            }
            redisClient.set(RedisKeyPrefix.USER, userKey, authToken,
                    userConfig.getHisTokenExpire());
            sysUserDao.updateByPrimaryKey(sysUser);
            //更新白名单列表
            whiteObj.setLastLoginDate(new Date());
            whiteObj.setUpdateDate(new Date());
            whiteObj.setUpdateId(sysUser.getId());
            userLoginWhiteDao.updateByPrimaryKeySelective(whiteObj);
            sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(),"登陆成功");
            //删除当前用户的支付密码key
            String key = String.format("%s:%s%s", RedisKeyPrefix.USER, RedisKeyConstant.MER_PAY_PWD, sysUser.getId());
            redisClient.delete(key);
            return session;
        } catch (GlobalException e) {
            sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(), e.getInfo());
            throw e;
        } catch (Exception e) {
            sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(),"系统异常");
            throw e;
        }
    }

    private String buildUserHisTokenKey(String userId, String hisToken, String tokenIdentification) {
        return RedisKeyConstant.AUTH_TOKEN + userId + ":" + hisToken + ":" + tokenIdentification;
    }

    private String buildHisTokenKey(String hisToken, String tokenIdentification) {
        return RedisKeyConstant.HIS_TOKEN + hisToken + ":" + tokenIdentification;
    }

    private String buildUserKey(String userId, String tokenIdentification) {
        return RedisKeyConstant.USER_TOKEN + userId + ":" + tokenIdentification;
    }

    private String buildUserAuthTokenKey(String userId, String authToken, String tokenIdentification) {
        return RedisKeyConstant.AUTH_TOKEN + userId + ":" + authToken + ":" + tokenIdentification;
    }

    @Override
    public SessionModel checkSession(String authToken, String userId, int sysType, String reqIp) {
        return checkSession(authToken, userId, sysType, reqIp, true);
    }

    @Override
    public SessionModel checkSession(String authToken, String userId, int sysType, String reqIp, boolean limitIp) {
        String tokenIdentification = userConfig.getTokenIdentification();
        String userAuthTokenKey = buildUserAuthTokenKey(userId, authToken, tokenIdentification);
        String hisTokenKey = buildHisTokenKey(authToken, tokenIdentification);
        if (redisClient.exists(RedisKeyPrefix.USER, userAuthTokenKey)) {
            SessionModel session = (SessionModel) redisClient.get(RedisKeyPrefix.USER, userAuthTokenKey);
            if (session.getUserType() != sysType)
                throw new GlobalException(Cause.error_login_noauth);
            if (limitIp && (StringUtils.isBlank(reqIp) || !reqIp.equals(session.getLoginIp())))
                throw new GlobalException(Cause.error_login_noauth);

            String userKey = buildUserKey(userId, tokenIdentification);

            final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
            String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);

            redisClient.setExpiredTime(RedisKeyPrefix.USER, userAuthTokenKey,
                    userConfig.getTokenExpire());
            redisClient.setExpiredTime(RedisKeyPrefix.USER, userHisTokenKey,
                    userConfig.getTokenExpire());
            redisClient.setExpiredTime(RedisKeyPrefix.USER, userKey,
                    userConfig.getTokenExpire());
            return session;
        } else if (redisClient.exists(RedisKeyPrefix.USER, hisTokenKey)) {
            throw new GlobalException(Cause.error_login_secondlogin);
        } else {
            throw new GlobalException(Cause.error_login_token);
        }
    }

    @Override
    public void refreshSession(String userId) {
        String tokenIdentification = userConfig.getTokenIdentification();
        String userKey = buildUserKey(userId, tokenIdentification);

        if (redisClient.exists(RedisKeyPrefix.USER, userKey)) {
            final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
            String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);
            if (redisClient.exists(RedisKeyPrefix.USER, userHisTokenKey)) {
                SessionModel session = (SessionModel) redisClient.get(RedisKeyPrefix.USER, userHisTokenKey);
                SysUser sysUser = sysUserDao.selectByPrimaryKey(userId);
                session.setBindEmail(sysUser.getBindEmail());
                session.setBindGoogle(sysUser.getBindGoogle());
                session.setBindPhone(sysUser.getBindPhone());
                session.setEmail(sysUser.getEmail());
                session.setGoogleSecret(sysUser.getGoogleSecret());
                session.setPhone(sysUser.getPhone());
                session.setResetFlag(sysUser.getResetFlag());
                session.setNickName(sysUser.getNickName());
                session.setUserPassword(sysUser.getPassword());
                if (!StringUtils.isBlank(sysUser.getUserPayPass()) || session.getUserType() == 2) {
                    session.setBindPayPass(1);
                    session.setPayPass(sysUser.getUserPayPass());
                }
                redisClient.set(RedisKeyPrefix.USER, buildUserHisTokenKey(userId, session.getAuthToken(), tokenIdentification), session, userConfig.getTokenExpire());
            }
        }

    }

    public SessionModel getSession(String userId) {
        String tokenIdentification = userConfig.getTokenIdentification();
        String userKey = buildUserKey(userId, tokenIdentification);
        if (redisClient.exists(RedisKeyPrefix.USER, userKey)) {
            final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
            String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);
            if (redisClient.exists(RedisKeyPrefix.USER, userHisTokenKey)) {
                return (SessionModel) redisClient.get(RedisKeyPrefix.USER, userHisTokenKey);
            } else {
                log.warn("userHisTokenKey[{}] not exists", userHisTokenKey);
            }
        } else {
            log.warn("userKey[{}] not exists", userKey);
        }
        throw new GlobalException(Cause.error_login_token);
    }

    @Override
    public boolean checkUserLogin(String userId, String authToken) {
        String tokenIdentification = userConfig.getTokenIdentification();
        String userAuthTokenKey = buildUserAuthTokenKey(userId, authToken, tokenIdentification);
        if (redisClient.exists(RedisKeyPrefix.USER, userAuthTokenKey)) {
            return true;
        } else if (redisClient.exists(RedisKeyPrefix.USER, buildHisTokenKey(authToken, tokenIdentification))) {
            return false;
        } else {
            return false;
        }
    }

    @Override
    public void deleteSession(String userId, HttpServletRequest httpServletRequest) {
        //退出后删除当前用户的支付密码key
        String key = String.format("%s:%s%s", RedisKeyPrefix.USER, RedisKeyConstant.MER_PAY_PWD, userId);
        redisClient.delete(key);

        String tokenIdentification = userConfig.getTokenIdentification();
        String userKey = buildUserKey(userId, tokenIdentification);
        if (redisClient.exists(RedisKeyPrefix.USER, userKey)) {
            final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
            String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);
            redisClient.delete(RedisKeyPrefix.USER, userHisTokenKey);
            final String reqIp = IpRemote.getIpAddr(httpServletRequest);
            taskExecutor.execute(new AsyncLogoutLogRunnable(Long.valueOf(userId), sysUserDao, sysLoginLogDao,
                    currentSystemManager, reqIp));
        }
    }

    @Override
    public Map<String, String> queryBindGoogle(String userId) {
        Map<String, String> resultMap = new HashMap<>();
        SessionModel session = getSession(userId);
        if (session.getBindGoogle() == 1 || !StringUtils.isBlank(session.getGoogleSecret()))
            throw new GlobalException(Cause.error_google_secret_bind);
        String googleSecret;
        if (redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_SECRET + userId))
            googleSecret = (String) redisClient.get(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_SECRET + userId);
        else {

            googleSecret = GoogleAuthenticator.generateSecretKey();
            redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_SECRET + userId, googleSecret,
                    userConfig.getGoogleSecretExpire());
        }
        resultMap.put("QRBarcode", GoogleAuthenticator.getQRBarcode(session.getLoginName(), googleSecret));
        resultMap.put("QRBarUrl",
                GoogleAuthenticator.getQRBarcodeURL(session.getLoginName(), "Billions", googleSecret));
        return resultMap;
    }

    @Override
    public Map<String, String> refreshBindGoogle(String userId) {
        Map<String, String> resultMap = new HashMap<>();
        SessionModel session = getSession(userId);
        if (session.getBindGoogle() == 1 || !StringUtils.isBlank(session.getGoogleSecret()))
            throw new GlobalException(Cause.error_google_secret_bind);

        String googleSecret = GoogleAuthenticator.generateSecretKey();
        redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_SECRET + userId, googleSecret,
                userConfig.getGoogleSecretExpire());
        resultMap.put("QRBarcode", GoogleAuthenticator.getQRBarcode(userId, googleSecret));
        resultMap.put("QRBarUrl", GoogleAuthenticator.getQRBarcodeURL(userId, "Billions", googleSecret));
        return resultMap;
    }

    @Override
    public boolean bindGoogle(String userId, GoogleVo reqVo) {
        SessionModel session = getSession(userId);
        if (session.getBindGoogle() == 1 || !StringUtils.isBlank(session.getGoogleSecret()))
            throw new GlobalException(Cause.error_google_secret_bind);
        if (!redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_SECRET + userId))
            throw new GlobalException(Cause.error_google_secret_exprice);
        final String googleSecret = (String) redisClient.get(RedisKeyPrefix.USER,
                RedisKeyConstant.GOOGLE_SECRET + userId);
        if (googleSecret == null) throw new GlobalException(Cause.error_google_secret_exprice);

        if (GoogleAuthenticator.check_code(googleSecret, getGoogleCode(reqVo.getGoogleCode()),
                System.currentTimeMillis())) {
            SysUser sysUser = new SysUser();
            sysUser.setId(Long.parseLong(userId));
            sysUser.setGoogleSecret(googleSecret);
            sysUser.setBindGoogle(1);
            sysUserDao.updateByPrimaryKeySelective(sysUser);
            refreshSession(userId);
        } else
            throw new GlobalException(Cause.error_google_secret_code);
        return true;
    }

    @Override
    public boolean removeGoogle(String userId, GoogleVo reqVo) {
        SessionModel session = getSession(userId);
        if (session.getIsSys() != 1)
            throw new GlobalException(Cause.error_password_noauth);
        if (session.getBindGoogle() == 0 || StringUtils.isBlank(session.getGoogleSecret()))
            throw new GlobalException(Cause.error_google_secret_remove);
        if (checkGoogle(reqVo.getGoogleCode())) {
            SysUser sysUser = new SysUser();
            sysUser.setId(Long.parseLong(userId));
            sysUser.setGoogleSecret("");
            sysUser.setBindGoogle(0);
            sysUserDao.updateByPrimaryKeySelective(sysUser);
            refreshSession(userId);
        }
        return true;
    }

    @Override
    public boolean changePassword(String userId, PasswordVo reqVo) {
        SessionModel session = getSession(userId);
        SysUser sysUser = null;
        if (session.getIsSys() != 1 && session.getResetFlag() != 1 && session.getUserType() != 3)
            throw new GlobalException(Cause.error_password_noauth);
        if (!StringUtils.isBlank(session.getGoogleSecret()) && StringUtils.isBlank(reqVo.getGoogleCode())) {
            throw new GlobalException(Cause.error_login_needgoolge);
        }
        checkGoogle(reqVo.getGoogleCode());
        if (StringUtils.isBlank(reqVo.getChildUserId())) {
            sysUser = sysUserDao.selectByPrimaryKey(userId);
            if (session.getResetFlag() != 1) {
                if (!EncryptionUtil.matches(reqVo.getOldPassword(), sysUser.getPassword())) {
                    throw new GlobalException(Cause.error_password_old);
                }
//				checkPayPass(reqVo.getOldPassword().toLowerCase(),Cause.error_password_old);
            }
            sysUser.setResetFlag(2);
        } else {
            sysUser = sysUserDao.selectByPrimaryKey(reqVo.getChildUserId());
            if (sysUser == null || sysUser.getExtendId() == null || session.getExtendId() == null
                    || sysUser.getExtendId().longValue() != session.getExtendId().longValue()
                    || session.getUserType() != sysUser.getUserType().intValue())
                throw new GlobalException(Cause.error_password_auth);
            sysUser.setResetFlag(1);
        }

        sysUser.setPassword(EncryptionUtil.encode().encode(reqVo.getNewPassword().toLowerCase()));
        sysUserDao.updateByPrimaryKeySelective(sysUser);
        refreshSession(sysUser.getId().toString());
        return true;

    }

    @Override
    public boolean changePayPassword(String userId, PasswordVo reqVo) {
        SessionModel session = getSession(userId);
        SysUser sysUser = null;
        if (session.getIsSys() != 1 && session.getBindPayPass() != 0)
            throw new GlobalException(Cause.error_password_noauth);
        if (StringUtils.isBlank(session.getGoogleSecret()))
            throw new GlobalException(Cause.error_auth_binGoogle);
        if (!StringUtils.isBlank(session.getGoogleSecret()) && StringUtils.isBlank(reqVo.getGoogleCode())) {
            throw new GlobalException(Cause.error_login_needgoolge);
        }

        checkGoogle(reqVo.getGoogleCode());

        if (StringUtils.isBlank(reqVo.getChildUserId())) {
            sysUser = sysUserDao.selectByPrimaryKey(userId);
            if (session.getBindPayPass() == 1) {
                checkPayPass(reqVo.getOldPassword().toLowerCase(), Cause.error_password_old);
            }
            sysUser.setUserPayPass(EncryptionUtil.encode().encode(reqVo.getNewPassword().toLowerCase()));
            sysUserDao.updateByPrimaryKeySelective(sysUser);
        } else {
            sysUser = sysUserDao.selectByPrimaryKey(reqVo.getChildUserId());
            if (sysUser == null || sysUser.getExtendId() == null || session.getExtendId() == null
                    || sysUser.getExtendId().longValue() != session.getExtendId().longValue()
                    || session.getUserType() != sysUser.getUserType().intValue())
                throw new GlobalException(Cause.error_password_auth);
            sysUser.setUserPayPass(EncryptionUtil.encode().encode(reqVo.getNewPassword().toLowerCase()));
            sysUserDao.updateByPrimaryKeySelective(sysUser);
        }
        refreshSession(sysUser.getId().toString());
        return true;
    }

    private Long getGoogleCode(String code) {
        Long googleCode;
        try {
            googleCode = Long.parseLong(code);
        } catch (Exception e) {
            throw new GlobalException(Cause.error_google_secret_code);
        }
        return googleCode;
    }

    @Transactional
    @Override
    public boolean checkPayPass(String payPass, Cause... causes) {
        SessionModel session = ContextHolder.currentUserInfo();
        if (StringUtils.isBlank(payPass)) {
            throw new IllegalArgumentException("用户输入支付密码为空");
        }
        if (session == null) {
            throw new IllegalArgumentException("无法识别当前登录用户");
        }
        if (!EncryptionUtil.matches(payPass.toLowerCase(), session.getPayPass())) {
            int errorNum = 1;
            if (redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.PAYPASS_ERROR + session.getUserId())) {
                errorNum = (Integer) redisClient.get(RedisKeyPrefix.USER,
                        RedisKeyConstant.PAYPASS_ERROR + session.getUserId()) + 1;
            }

            redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.PAYPASS_ERROR + session.getUserId(), errorNum,
                    userConfig.getPayPassErrorExpire());
            if (errorNum > userConfig.getMaxPayPassError()) {
                deleteSession(session.getUserId().toString());
            }
            if (causes != null && causes.length != 0) {
                throw new GlobalException(causes[0]);
            }
            throw new GlobalException(Cause.error_password_pay);
        } else {
            redisClient.delete(RedisKeyPrefix.USER, RedisKeyConstant.PAYPASS_ERROR + session.getUserId());
        }
        return true;
    }

    private void deleteSession(String userId) {
        String tokenIdentification = userConfig.getTokenIdentification();
        String userKey = buildUserKey(userId, tokenIdentification);
        if (redisClient.exists(RedisKeyPrefix.USER, userKey)) {
            final String hisToken = (String) redisClient.get(RedisKeyPrefix.USER, userKey);
            String userHisTokenKey = buildUserHisTokenKey(userId, hisToken, tokenIdentification);
            redisClient.delete(RedisKeyPrefix.USER, userHisTokenKey);
        }
    }

    @Override
    public boolean checkGoogle(Long userId, String googleSecret, String googleCode) {
        if (!StringUtils.isBlank(googleSecret) && !GoogleAuthenticator.check_code(googleSecret,
                getGoogleCode(googleCode), System.currentTimeMillis())) {
            int errorNum = 1;
            if (redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + userId)) {
                errorNum = (Integer) redisClient.get(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + userId) + 1;
            }
            redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + userId, errorNum, userConfig.getGoogleErrorExpire());
            if (errorNum >= userConfig.getMaxGoogleError()) {
                deleteSession(userId.toString());
            }
            throw new GlobalException(Cause.error_google_secret_code);
        } else if (!StringUtils.isBlank(googleSecret) && GoogleAuthenticator.check_code(googleSecret,
                getGoogleCode(googleCode), System.currentTimeMillis())) {
            redisClient.delete(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + userId);
        }
        return true;
    }

    @Override
    public SysUser beforeLoginCheck(SysUser reqVo, boolean limitIp, String reqIp) {
        Example example = new Example(SysUser.class);
        example.createCriteria().andEqualTo("userName", reqVo.getUserName()).andEqualTo("userType", reqVo.getUserType()).andEqualTo("delFlag", false);
        SysUser sysUser = sysUserDao.selectOneByExample(example);
        if (sysUser == null) {
            throw new GlobalException(Cause.error_login);
        }
        try {
            /** 校验商户，分销商和 代理状态 **/
//            if (reqVo.getUserType() == 1) {
//                Merchant merchant = merchantDao.selectByPrimaryKey(sysUser.getExtendId());
//                if (merchant == null || merchant.getDelFlag() || merchant.getState() != 1)
//                    throw new GlobalException(Cause.error_status_mer);
//                if (merchant.getLockFlag() != 1)
//                    throw new GlobalException(Cause.error_lock_mer);
//            } else if (reqVo.getUserType() == 2) {
//                Agent agent = agentDao.selectByPrimaryKey(sysUser.getExtendId());
//                if (agent == null || agent.getDelFlag() || agent.getState() != 1)
//                    throw new GlobalException(Cause.error_status_agent);
//                if (agent.getLockFlag() != 1)
//                    throw new GlobalException(Cause.error_lock_agent);
//            }else if (reqVo.getUserType() == 4) {
                /*Agent agent = agentDao.selectByPrimaryKey(sysUser.getExtendId());
                if (agent == null || agent.getDelFlag() || agent.getState() != 1)
                    throw new GlobalException(Cause.error_status_agent);
                if (agent.getLockFlag() != 1)
                    throw new GlobalException(Cause.error_lock_agent);*/
//            }



            /** 判断用户是否被禁用 **/
            if (sysUser.getState() == null || sysUser.getState() != 1)
                throw new GlobalException(Cause.error_login_del);

            /** 判断用户是否被锁定 **/
            if (sysUser.getLockFlag() != 1)
                throw new GlobalException(Cause.error_login_lock);

            /** 校验绑定IP，商户有自己维护的白名单表，其他系统都是用的sys_user表的bindIp **/
            int type = reqVo.getUserType();

            Example whiteExample = new Example(UserLoginWhite.class);
            Example.Criteria whiteCriteria = whiteExample.createCriteria();
            whiteCriteria.andEqualTo("ip", reqIp).andEqualTo("userType", reqVo.getUserType());
            if(type != SystemConstants.SYS_TYPE_ADMIN){
                whiteCriteria.andEqualTo("extendId", sysUser.getExtendId());
            }

            UserLoginWhite whiteObj = userLoginWhiteDao.selectOneByExample(whiteExample);
            if (whiteObj == null) {
                throw new GlobalException(Cause.error_login_iperror);
            }

            /*String bindIp = sysUser.getUserBindIp();
            if (sysUser.getIsSys().intValue() != 1 && reqVo.getUserType() != 3) {
                SysUser sysUser2 = new SysUser();
                sysUser2.setExtendId(sysUser.getExtendId());
                sysUser2.setUserType(sysUser.getUserType());
                sysUser2.setState(1);
                sysUser2.setIsSys(1);
                sysUser2 = sysUserDao.selectOne(sysUser2);
                if (sysUser2 == null)
                    throw new GlobalException(Cause.error_login_del);
                bindIp = sysUser2.getUserBindIp();
                if (limitIp && !StringUtils.isBlank(bindIp) && bindIp.indexOf(reqIp) < 0)
                    throw new GlobalException(Cause.error_login_iperror);
            }*/

            // 密码错误时记录日志（目前找不到别的地方记，所以先记录在这里）
            if (!EncryptionUtil.matches(reqVo.getPassword(), sysUser.getPassword())) {
                sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(),  Cause.error_login.getInfo());
            }

            return sysUser;
        } catch (GlobalException e) {
            sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(),  e.getInfo());
            throw e;
        } catch (Exception e) {
            sysLogManager.add(sysUser, reqIp, SysLoginTypeEnum.login.getName(), "系统异常");
            throw e;
        }

    }

    @Override
    public boolean checkGoogle(String googleCode) {
        SessionModel session = ContextHolder.currentUserInfo();

        if (StringUtils.isEmpty(session.getGoogleSecret())) throw new GlobalException(Cause.error_auth_binGoogle);

        if (!StringUtils.isBlank(session.getGoogleSecret()) && !GoogleAuthenticator
                .check_code(session.getGoogleSecret(), getGoogleCode(googleCode), System.currentTimeMillis())) {
            int errorNum = 1;
            if (redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + session.getUserId())) {
                errorNum = (Integer) redisClient.get(RedisKeyPrefix.USER,
                        RedisKeyConstant.GOOGLE_ERROR + session.getUserId()) + 1;
            }
            redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + session.getUserId(), errorNum,
                    userConfig.getGoogleErrorExpire());
            if (errorNum >= userConfig.getMaxGoogleError()) {
                deleteSession(session.getUserId().toString());
            }
            throw new GlobalException(Cause.error_google_secret_code);
        } else if (!StringUtils.isBlank(session.getGoogleSecret()) && GoogleAuthenticator
                .check_code(session.getGoogleSecret(), getGoogleCode(googleCode), System.currentTimeMillis())) {
            redisClient.delete(RedisKeyPrefix.USER, RedisKeyConstant.GOOGLE_ERROR + session.getUserId());
        }
        return true;
    }

    @Override
    public boolean checkLoginPassword(String loginPass ,  Cause... causes) {
        SessionModel session = ContextHolder.currentUserInfo();
        if (StringUtils.isBlank(loginPass)) {
            throw new IllegalArgumentException("用户输入密码为空");
        }
        if (session == null) {
            throw new IllegalArgumentException("无法识别当前登录用户");
        }
        if (!EncryptionUtil.matches(loginPass, session.getUserPassword())) {
            int errorNum = 1;
            if (redisClient.exists(RedisKeyPrefix.USER, RedisKeyConstant.LOGIN_FAILD + session.getUserId())) {
                errorNum = (Integer) redisClient.get(RedisKeyPrefix.USER,
                        RedisKeyConstant.LOGIN_FAILD + session.getUserId()) + 1;
            }

            redisClient.set(RedisKeyPrefix.USER, RedisKeyConstant.LOGIN_FAILD + session.getUserId(), errorNum,
                    userConfig.getPayPassErrorExpire());
            if (errorNum > userConfig.getMaxPayPassError()) {
                deleteSession(session.getUserId().toString());
            }
            if (causes != null && causes.length != 0) {
                throw new GlobalException(causes[0]);
            }
            throw new GlobalException(Cause.update_user_password_is_incorrect);
        } else {
            redisClient.delete(RedisKeyPrefix.USER, RedisKeyConstant.LOGIN_FAILD + session.getUserId());
        }
        return true;
    }

}
